<?php
namespace Tbf\Bundle\YntAuthBundle\Douban;
use Guzzle\Http\Client;
use Guzzle\Http\Exception\ClientErrorResponseException;
use Guzzle\Http\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Tbf\Bundle\CallbackSerializerBundle\CallbackSerializer;
use Tbf\Bundle\SerializerBundle\Serializer;
use Tbf\Bundle\YntAuthBundle\MediaImpInterface;
use Tbf\Bundle\YntAuthBundle\Model\LoginRequest;
use Tbf\Bundle\YntAuthBundle\Model\LoginResponse;
use Tbf\Bundle\YntAuthBundle\Model\ShareRequest;
use Tbf\Bundle\YntAuthBundle\Service\MediaManager;
use Tbf\Bundle\YntAuthBundle\Service\TransactionManager;
use Tbf\Component\Arrays\ArrayFunction;
use Tbf\Component\Crypto\CryptoRandUtil;
use Tbf\Component\FileSystem\FileSystemFunction;
use Tbf\Component\Path\PathFunction;

class DoubanImp implements MediaImpInterface{
    protected $config;
    protected $http;
    protected $serializer;
    protected $transaction_manager;
    function __construct(Config $config,
                         Client $http,
                         Serializer $serializer,
                         TransactionManager $transaction_manager
        ){
        $this->config = $config;
        $this->http = $http;
        $this->serializer = $serializer;
        $this->transaction_manager = $transaction_manager;
    }

    /**
     * @param LoginRequest $login_request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    function login(LoginRequest $login_request)
    {
        //获取authorization_code
        $transaction = $this->transaction_manager->newTransaction();
        $transaction->setLoginCallback($login_request->getPageCallback());
        $transaction->setMedia($login_request->getMedia());
        $transaction->setExtraData($login_request->getExtraData());
        $this->transaction_manager->save($transaction);

        $url = Url::factory($this->config->getOauth2AuthUrl());
        $url->setQuery(array(
            'client_id'=>$this->config->getAppKey(),
            'redirect_uri'=>$this->config->getOauth2CallbackUrl(),
            'response_type'=>'code',
            'state'=>$transaction->getId(),
        ));
        return new RedirectResponse((string)$url);
    }

    /**
     * @param Request $request
     * @return Response
     * @throws \Exception
     */
    function onLoginCallback(Request $request){
        $input = $request->query->all();
        if (!empty($input['error'])){
            return new Response(sprintf('豆瓣返回错误: %s',$input['error']));
        }
        $err = ArrayFunction::checkParameterExist($input,array('code','state'));
        if ($err!==null){
            return new Response($err);
        }
        $transaction_id = $input['state'];
        $transaction = $this->transaction_manager->get($transaction_id);
        if (empty($transaction)){
            return new Response('事务不存在');
        }
        //获取access_token
        try{
            $resp = $this->http->createRequest('POST',$this->config->getOauth2TokenUrl(),null,array(
                'client_id'=>$this->config->getAppKey(),
                'client_secret'=>$this->config->getAppSecret(),
                'redirect_uri'=>$this->config->getOauth2CallbackUrl(),
                'grant_type'=>'authorization_code',
                'code'=>$input['code']
            ))->send();
        }catch(ClientErrorResponseException $e){
            $ret  = $e->getMessage()."\n";
            $ret .= '---------------------'."\n";
            $ret .= $e->getRequest()."\n";
            $ret .= '---------------------'."\n";
            $ret .= $e->getResponse()."\n";
            throw new \Exception($ret);
        }
        $access_token_data = json_decode($resp->getBody(true),true);

        $transaction->setAccessToken( $access_token_data['access_token']);
        $transaction->setRefreshToken( $access_token_data['refresh_token']);

        $response = new LoginResponse();
        $response->setToken($transaction->getAccessToken());
        $response->setMedia($transaction->getMedia());
        $response->setExtraData($transaction->getExtraData());
        if (!empty($access_token_data['douban_user_id'])){
            $response->setMediaUserID($access_token_data['douban_user_id']);
            $transaction->setMediaUserId($access_token_data['douban_user_id']);
        }
        if (!empty($access_token_data['douban_user_name'])){
            $response->setScreenName($access_token_data['douban_user_name']);
        }
        $this->transaction_manager->save($transaction);

        $response->setTransactionId($transaction->getId());
        $callback = $transaction->getLoginCallback();
        return $callback->invoke($response);
    }

    /**
     * @param string $transaction_id
     * @return null|\Tbf\Bundle\YntAuthBundle\Model\User
     */
    function getCurrentUserInfo($transaction_id){
        $transaction = $this->transaction_manager->get($transaction_id);
        if (empty($transaction)){
            return null;
        }
        $data = $this->apiCall('getCurrentUserInfo',array(),$transaction->getAccessToken());
        $data = ArrayFunction::keyTran($data,array(
            'uid'=>'mediaUserID',
            'id'=>'mediaUserID',
            'name'=>'screenName',
            'created'=>'createTime',
            'alt'=>'homePage',
            'avatar'=>'avatar',
            'loc_name'=>'location',
            'desc'=>'description',
            'followers_count'=>'followersCount',
            'statuses_count'=>'statusesCount',
            'icon_avatar'=>'iconAvatar',
            'large_avatar'=>'largeAvatar'
        ));
        $user =  $this->serializer->newFromArray('Tbf\Bundle\YntAuthBundle\Model\User',$data);
        /** @var \Tbf\Bundle\YntAuthBundle\Model\User $user */
        $user->setMedia($transaction->getMedia());
        $transaction->setMediaUserId($user->getMediaUserID());
        $this->transaction_manager->save($transaction);
        return $user;
    }

    /**
     * TODO content以@开头问题
     * @param ShareRequest $share
     * @return error
     */
    function share(ShareRequest $share){
        $transaction = $this->transaction_manager->get($share->getTransactionId());
        if (empty($transaction)){
            return '事务不存在';
        }
        if ($share->getUrl()){
            $content = $share->getContent().' '.$share->getUrl();
        }else{
            $content = $share->getContent();
        }
        if ($share->getImageUrl()){
            $temp_file_path = FileSystemFunction::getTmpFilePath(PathFunction::getExtension($share->getImageUrl()));
            $image_content = file_get_contents($share->getImageUrl());
            FileSystemFunction::putContent($temp_file_path,$image_content);
            $image = '@'.$temp_file_path;
        }else{
            $image = null;
        }
        $this->apiCall('share',array(
            'source'=>$this->config->getAppKey(),
            'text'=>$content,
            'image'=>$image
        ),$transaction->getAccessToken());
        if (!empty($temp_file_path)){
            FileSystemFunction::delete($temp_file_path);
        }
        return null;
    }

    /**
     * 内部调用api,
     * 在配置那里配置某个api的http_method和url
     * @param $name
     * @param $data
     * @param $token
     * @return mixed
     * @throws \Exception
     */
    protected function apiCall($name,$data,$token){
        $data = ArrayFunction::removeNullValue($data);
        $api_data = $this->config->getApiData($name);
        try{
            $resp = $this->http->createRequest($api_data['http_method'],$api_data['url'],array(
                'Authorization'=>'Bearer '.$token
            ),$data)->send();
        }catch(ClientErrorResponseException $e){
            $ret  = $e->getMessage()."\n";
            $ret .= '---------------------'."\n";
            $ret .= $e->getRequest()."\n";
            $ret .= '---------------------'."\n";
            $ret .= $e->getResponse()."\n";
            throw new \Exception($ret);
        }
        $data = json_decode($resp->getBody(true),true);
        return $data;
    }

    function getName(){
        return 'douban';
    }
}